package gui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;

import javax.swing.JPanel;

import model.CardInterface;
import toolkit.Paint;

public class PlayTable extends JPanel
{
	private int positions;
	
	private int padding;
	
	private int tableWidth, tableHeight;

	private int componentWidth, componentHeight;
	
	private ArrayList<Seat> seats = new ArrayList<Seat>();
	
	private CardContainer board;
	
	public PlayTable()
	{
		setLayout(new TableLayout());
	}
	
	public void prepareLayout()
	{
		for (int i = 0; i < positions; i++)
		{
			Seat seat = new Seat();
			
			Dimension d = seat.getPreferredSize();
			
			if (i == 0)
			{
				padding = (int) Math.max(d.width, d.height) / 2 + 20;
			}
			
			seats.add(seat);
			
			add(seat);
		}
		
		board = new CardContainer();
		add(board);
	}
	
	public void setBoardCards(ArrayList<CardInterface> cards)
	{
		board.setCards(cards);
	}
	
	public void setPositions(int positions)
	{
		this.positions = positions;
	}
	
	public ArrayList<Seat> getSeats()
	{
		return seats;
	}
	
	private class TableLayout implements LayoutManager
	{
		public void addLayoutComponent(String name, Component comp)
		{
			
		}

		public void layoutContainer(Container parent)
		{
			Insets insets = parent.getInsets();
			
			componentWidth = parent.getWidth() - insets.left - insets.right;
			componentHeight = parent.getHeight() - insets.top - insets.bottom;
			
			tableWidth = componentWidth - padding * 2;
			tableHeight = componentHeight - padding * 2;
			
			ArrayList<Point> points = getOvalPoints(padding, padding, tableWidth, tableHeight, positions);
			
			for (int i = 0; i < seats.size(); i++)
			{
				Point p;
				
				try
				{
					p = points.get(i);
				}
				catch (IndexOutOfBoundsException e)
				{
					break;
				}
				
				Seat seat = seats.get(i);
				
				Dimension d = seat.getPreferredSize();
				seat.setBounds(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
			}
			
			Dimension d = board.getPreferredSize();
			board.setBounds(componentWidth / 2 - d.width / 2, componentHeight / 2 - d.height / 2, d.width, d.height);
		}

		public Dimension minimumLayoutSize(Container parent)
		{
			return new Dimension(717, 620);
			//return preferredLayoutSize(parent);
		}

		public Dimension preferredLayoutSize(Container parent)
		{
			return new Dimension(parent.getWidth(), parent.getHeight());
		}

		public void removeLayoutComponent(Component comp)
		{
			
		}
	}
	
	public void paintComponent(Graphics g)
	{
		Paint paint = new Paint(g);
		
		Graphics2D g2 = paint.newLayer();
		
		Ellipse2D table = new Ellipse2D.Double(padding, padding, tableWidth, tableHeight);
		
		g2.setPaint(new GradientPaint(0, 0, new Color(0, 128, 0), tableWidth, 0, new Color(100, 180, 100)));
		g2.fill(table);
		g2.setColor(Color.BLACK);
		g2.setStroke(new BasicStroke(5));
		g2.draw(table);
	}

	public ArrayList<Point> getOvalPoints(int cx, int cy, int width, int height, int spots)
	{
		//increase the width so we can calculate the absolute seat position later
		int ovalWidth = (int) width + (int) cx * 2;

		boolean even = spots % 2 == 0;
		
		spots /= 2;

		spots += 1;

		width /= 2;
		height /= 2;
		
		if (width <= 0) width = 1;
		if (height <= 0) height = 1;
		
		cx += width;
		cy += height;
		
		double c = 1; 
        double oldx = 0, oldy1 = 0;
		double totalDistance = 0;
		double lengthD = 0, widthD = 0, theX = 0, theY = 0;
		double y1, y2;
		double x1 = cx - c * width;
		double currentDistance = 0;
		double x2 = cx + c * width;
		double dx =(x2 - x1) / ((width / height) * 256 * height);
		int cStep = 0;
	    
	    ArrayList<Point> points = new ArrayList<Point>();
	    
		for (double x = x1; x <= x2; x += dx)
		{
			y1 = cy + height * Math.sqrt(c * c - (x - cx) * (x - cx) / (width * width));
			y2 = cy - height * Math.sqrt(c * c - (x - cx) * (x - cx) / (width * width));
			
			theX = x;
			theY = y1;
			
			if (oldx != 0)
			{
				lengthD = oldx - theX;
				widthD = oldy1 - theY;
				totalDistance += Math.sqrt((lengthD * lengthD) + (widthD * widthD));
			}
			
			oldx = theX;
			oldy1 = theY;
		}
		
		double stepDistance = 0;
		
		if (even)
		{
			stepDistance = (totalDistance / (spots - 1));
		}
		else
		{
			stepDistance = (totalDistance / spots);
			
			// Add half the step distance in such a way that all the steps together have the distance of 1 step added
			// remove 0.5 from the seat number because this will compensate for the redundant steps
			stepDistance += stepDistance / ((spots - 0.5) * 2);
		}
		
		theX = 0;
		theY = 0;
		lengthD = 0;
		widthD = 0;
		oldx = 0;
		oldy1 = 0;
		cStep = 0;
		
		// Loop again to calculate seat positions.
		for (double x = x1; x <= x2; x += dx)
		{
			y1 = cy + height * Math.sqrt(c * c - (x - cx) * (x - cx) / (width * width));
			y2 = cy - height * Math.sqrt(c * c - (x - cx) * (x - cx) / (width * width));
			
			theX = x;
			theY = y1;
			
			if (oldx != 0)
			{
				lengthD = oldx - theX;
				widthD = oldy1 - theY;
				currentDistance += Math.sqrt((lengthD * lengthD) + (widthD * widthD));
			}
			
			oldx = theX;
			oldy1 = theY;
			
			if (even || cStep < spots)
			{
				if (currentDistance >= cStep * stepDistance)
				{
					Point p = new Point((int) theX, (int) theY);
					points.add(p);
				}
				
				theY = (int) y2;
				
				if (currentDistance >= cStep * stepDistance)
				{
					cStep++;
					
					if (even)
					{
						Point p = new Point(ovalWidth - (int) theX, (int) theY);
						points.add(p);
					}
					else
					{
						Point p = new Point((int) theX, (int) theY);
						points.add(p);
					}
				}
			}
		}
		
		// Remove redundant points.
		if (even)
		{
			points.remove(1);
		}
		
		points.remove(0);
	    
		return points;
	}
}